// src/time_source.cpp
#include "time_source.h"

#include <cmath>
#include <cstdint>
#include <cstdio>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <thread>
#include <chrono>

#if defined(_MSC_VER)
// MSVC
  #include <intrin.h>
  #define WIN32_LEAN_AND_MEAN
  #include <windows.h>
#elif defined(_WIN32)
// MinGW/Clang-cl on Windows
  #include <intrin.h>
  #define WIN32_LEAN_AND_MEAN
  #include <windows.h>
#else
// Linux/Unix
  #include <x86intrin.h>
  #include <cpuid.h>
  #include <time.h>
#endif

// ----------------- 内部工具 -----------------

// 轻量封装：steady_clock 的 ns（外部参考）
static inline int64_t steady_now_ns_inline() {
  using namespace std::chrono;
  return (int64_t)duration_cast<nanoseconds>(
           steady_clock::now().time_since_epoch()).count();
}

// Invariant TSC 检测
static bool has_invariant_tsc() {
#if defined(_WIN32)
  int regs[4] = {0};
  __cpuid(regs, 0x80000007);
  // AMD/Intel: EDX bit 8 = Invariant TSC
  return (regs[3] & (1 << 8)) != 0;
#else
  unsigned int eax, ebx, ecx, edx;
  if (!__get_cpuid(0x80000007, &eax, &ebx, &ecx, &edx)) return false;
  return (edx & (1u << 8)) != 0;
#endif
}

// 读取 CPUID 0x15/0x16 获取 TSC 频率（GHz）
static bool cpuid_read_tsc_ghz(double& ghz) {
#if defined(_WIN32)
  int r[4]; // eax, ebx, ecx, edx
  __cpuid(r, 0x15);
  unsigned eax = (unsigned)r[0], ebx = (unsigned)r[1], ecx = (unsigned)r[2];
#else
  unsigned int eax, ebx, ecx, edx;
  if (!__get_cpuid(0x15, &eax, &ebx, &ecx, &edx)) return false;
#endif
  if (eax == 0 || ebx == 0) return false; // 无法提供

#if defined(_WIN32)
  int r2[4]; __cpuid(r2, 0x16);
  unsigned baseMHz = (unsigned)r2[0]; // EAX: base MHz
  if (baseMHz) { ghz = (double)baseMHz / 1000.0; return true; }
  // 0x16 不可靠则用 0x15：TSC = crystal * (ebx/eax)，ecx=crystal Hz
  if (ecx) { ghz = (double)ecx * ((double)ebx / (double)eax) / 1e9; return true; }
  return false;
#else
  if (ecx) { ghz = (double)ecx * ((double)ebx / (double)eax) / 1e9; return true; }
  return false;
#endif
}

// 自校准：用稳定时钟对时一小段时间，估 TSC 频率（GHz）
static double calibrate_ghz(double seconds) {
#if defined(_WIN32)
  LARGE_INTEGER fq, t0, t1;
  QueryPerformanceFrequency(&fq);
  QueryPerformanceCounter(&t0);
  _mm_lfence(); unsigned aux0; uint64_t c0 = __rdtscp(&aux0); _mm_lfence();
  std::this_thread::sleep_for(std::chrono::duration<double>(seconds));
  _mm_lfence(); unsigned aux1; uint64_t c1 = __rdtscp(&aux1); _mm_lfence();
  QueryPerformanceCounter(&t1);
  double real = (double)(t1.QuadPart - t0.QuadPart) / (double)fq.QuadPart;
#else
  timespec ts0, ts1;
  clock_gettime(CLOCK_MONOTONIC_RAW, &ts0);
  _mm_lfence(); unsigned aux0; uint64_t c0 = __rdtscp(&aux0); _mm_lfence();
  std::this_thread::sleep_for(std::chrono::duration<double>(seconds));
  _mm_lfence(); unsigned aux1; uint64_t c1 = __rdtscp(&aux1); _mm_lfence();
  clock_gettime(CLOCK_MONOTONIC_RAW, &ts1);
  double real = (ts1.tv_sec - ts0.tv_sec) + (ts1.tv_nsec - ts0.tv_nsec)/1e9;
#endif
  if (real <= 0.0) real = seconds; // 防御
  double ghz = (double)(c1 - c0) / real / 1e9;
  return ghz;
}

// ----------------- TscSource 实现 -----------------

// 静态成员（由头文件声明）
TscCalib TscSource::g_calib{};

// 懒初始化：首次使用时保证 init() 至少执行一次
static std::once_flag g_tsc_init_once;
static void ensure_inited() {
  std::call_once(g_tsc_init_once, []{
    (void)TscSource::init(0.3); // ~0.3s 窗口校准，较稳
  });
}

TscCalib TscSource::init(double calib_seconds) {
  TscCalib r{};
  if (!has_invariant_tsc()) {
    r.ok = false; r.ghz = 0.0;
    return (g_calib = r);
  }
  double ghz = 0.0;
  if (!cpuid_read_tsc_ghz(ghz) || ghz <= 0.1) {
    ghz = calibrate_ghz(calib_seconds);
  }
  r.ghz = ghz;
  r.ok  = (ghz > 0.1);
  g_calib = r;
  return r;
}

// 原始 TSC 读数（cycles）
uint64_t TscSource::now_ticks() {
#if defined(_WIN32) || defined(__x86_64__) || defined(__i386__)
  _mm_lfence(); unsigned aux; uint64_t t = __rdtscp(&aux); _mm_lfence();
  return t;
#else
  return 0; // 非 x86：无 TSC
#endif
}

// ticks → ns（ns = ticks / GHz）
// 带懒初始化 & 除零防护。若 ghz 无效，返回 0（调用方应确保已调用 init()）
int64_t TscSource::ticks_to_ns(uint64_t t) {
  ensure_inited();
  double f = g_calib.ghz;
  if (f > 0.0) return (int64_t)((double)t / f);
  return 0; // 回退策略：保持语义一致（传入的是“ticks差值”）
}

// 便捷访问
double TscSource::ghz() { return g_calib.ghz; }
bool   TscSource::ok()  { return g_calib.ok;  }

// ----------------- 可选 Demo -----------------
void run_tsc_demo(int N, int interval_ms) {
  using namespace std;
  auto cal = TscSource::init(0.3);
  if (!cal.ok) { cerr << "[ERR] Invariant TSC not available.\n"; return; }

  cout << fixed << setprecision(3);
  cout << "[TSC] freq = " << cal.ghz << " GHz\n";
  cout << "idx" << setw(16) << "ref_ns()"
               << setw(18) << "tsc_ns()"
               << setw(16) << "err_ns"
               << setw(12) << "inst_ppm" << "\n";

  _mm_lfence(); unsigned aux0; uint64_t t0 = __rdtscp(&aux0); _mm_lfence();
  int64_t  r0 = steady_now_ns_inline();
  uint64_t t_prev = t0; int64_t r_prev = r0;

  for (int i=1;i<=N;i++) {
    std::this_thread::sleep_for(std::chrono::milliseconds(interval_ms));
    _mm_lfence(); unsigned aux; uint64_t t = __rdtscp(&aux); _mm_lfence();
    int64_t  r = steady_now_ns_inline();

    int64_t ref_ns_total = r - r0;
    int64_t tsc_ns_total = TscSource::ticks_to_ns(t - t0);
    int64_t err_ns_total = tsc_ns_total - ref_ns_total;

    int64_t d_ref = r - r_prev;
    int64_t d_tsc = TscSource::ticks_to_ns(t - t_prev);
    double  inst_ppm = (d_ref != 0) ? ((double)(d_tsc - d_ref) / (double)d_ref * 1e6) : 0.0;

    cout << setw(3)  << i
         << setw(16) << ref_ns_total
         << setw(18) << tsc_ns_total
         << setw(16) << err_ns_total
         << setw(12) << inst_ppm << "\n";

    t_prev = t; r_prev = r;
  }

  _mm_lfence(); unsigned aux1; uint64_t t_last = __rdtscp(&aux1); _mm_lfence();
  int64_t  r_last = steady_now_ns_inline();
  int64_t  ref_ns_total = r_last - r0;
  int64_t  tsc_ns_total = TscSource::ticks_to_ns(t_last - t0);
  double   overall_ppm  = (ref_ns_total != 0)
    ? ((double)(tsc_ns_total - ref_ns_total) / (double)ref_ns_total * 1e6)
    : 0.0;

  cout << "Overall drift: " << overall_ppm << " ppm ("
       << (tsc_ns_total - ref_ns_total) << " ns over "
       << ref_ns_total << " ns)\n";
}

#ifdef TSC_DEMO_MAIN
int main() {
  run_tsc_demo(15, 200);
  return 0;
}
#endif
